css: Add animation support
authorBenjamin Otte <otte@redhat.com>
Mon, 10 Sep 2012 11:57:56 +0000 (13:57 +0200)
committerBenjamin Otte <otte@redhat.com>
Mon, 17 Sep 2012 18:39:12 +0000 (20:39 +0200)
This adds the GtkCssAnimation class and the code needed to hook it into
GtkStyleContext. It takes the values out of the CSS "animation"
properties and does animations. See
  http://dev.w3.org/csswg/css3-animations/
for details.

Note that the code for starting and stopping animations with widget
visibility doesn't work yet.

gtk/Makefile.am
gtk/gtkcssanimatedvalues.c
gtk/gtkcssanimatedvaluesprivate.h
gtk/gtkcssanimation.c [new file with mode: 0644]
gtk/gtkcssanimationprivate.h [new file with mode: 0644]
gtk/gtkstylecontext.c

index 1524b4b503f4d70f559ba30bf2065ddcd5b005b1..bb619a4bd76cfdcf4c64b8bbfaa8515a7d57220f 100644 (file)
@@ -427,6 +427,7 @@ gtk_private_h_sources =             \
        gtkcolorchooserprivate.h        \
        gtkcontainerprivate.h   \
        gtkcssanimatedvaluesprivate.h   \
+       gtkcssanimationprivate.h        \
        gtkcssarrayvalueprivate.h       \
        gtkcssbgsizevalueprivate.h      \
        gtkcssbordervalueprivate.h      \
@@ -648,6 +649,7 @@ gtk_base_c_sources =                \
        gtkcomboboxtext.c       \
        gtkcontainer.c          \
        gtkcssanimatedvalues.c  \
+       gtkcssanimation.c       \
        gtkcssarrayvalue.c      \
        gtkcssbgsizevalue.c     \
        gtkcssbordervalue.c     \
index c33784996869a4b1b12d46cff78ee38929447a9f..92bd1e48f00f2b7b661b077c5e7d9ea6d1a3c7ce 100644 (file)
 
 #include "gtkcssanimatedvaluesprivate.h"
 
+#include "gtkcssanimationprivate.h"
 #include "gtkcssarrayvalueprivate.h"
+#include "gtkcssenumvalueprivate.h"
 #include "gtkcssnumbervalueprivate.h"
 #include "gtkcssshorthandpropertyprivate.h"
 #include "gtkcssstringvalueprivate.h"
 #include "gtkcssstylepropertyprivate.h"
 #include "gtkcsstransitionprivate.h"
 #include "gtkstyleanimationprivate.h"
+#include "gtkstylepropertiesprivate.h"
 #include "gtkstylepropertyprivate.h"
+#include "gtkstyleproviderprivate.h"
 
 G_DEFINE_TYPE (GtkCssAnimatedValues, _gtk_css_animated_values, GTK_TYPE_CSS_COMPUTED_VALUES)
 
@@ -176,7 +180,6 @@ gtk_css_animated_values_start_transitions (GtkCssAnimatedValues *values,
   delays = _gtk_css_computed_values_get_value (computed, GTK_CSS_PROPERTY_TRANSITION_DELAY);
   timing_functions = _gtk_css_computed_values_get_value (computed, GTK_CSS_PROPERTY_TRANSITION_TIMING_FUNCTION);
 
-
   for (i = 0; i < GTK_CSS_PROPERTY_N_PROPERTIES; i++)
     {
       GtkStyleAnimation *animation;
@@ -215,20 +218,97 @@ gtk_css_animated_values_start_transitions (GtkCssAnimatedValues *values,
     }
 }
 
+static GtkStyleAnimation *
+gtk_css_animated_values_find_animation (GtkCssAnimatedValues *values,
+                                        const char           *name)
+{
+  GSList *list;
+
+  for (list = values->animations; list; list = list->next)
+    {
+      if (!GTK_IS_CSS_ANIMATION (list->data))
+        continue;
+
+      if (g_str_equal (_gtk_css_animation_get_name (list->data), name))
+        return list->data;
+    }
+
+  return NULL;
+}
+
+static void
+gtk_css_animated_values_start_css_animations (GtkCssAnimatedValues *values,
+                                              gint64                timestamp,
+                                              GtkStyleContext      *context)
+{
+  GtkCssComputedValues *computed;
+  GtkStyleProviderPrivate *provider;
+  GtkCssValue *durations, *delays, *timing_functions, *animations;
+  GtkCssValue *iteration_counts, *directions, *play_states, *fill_modes;
+  guint i;
+
+  computed = GTK_CSS_COMPUTED_VALUES (values);
+
+  provider = _gtk_style_context_get_style_provider (context);
+  animations = _gtk_css_computed_values_get_value (computed, GTK_CSS_PROPERTY_ANIMATION_NAME);
+  durations = _gtk_css_computed_values_get_value (computed, GTK_CSS_PROPERTY_ANIMATION_DURATION);
+  delays = _gtk_css_computed_values_get_value (computed, GTK_CSS_PROPERTY_ANIMATION_DELAY);
+  timing_functions = _gtk_css_computed_values_get_value (computed, GTK_CSS_PROPERTY_ANIMATION_TIMING_FUNCTION);
+  iteration_counts = _gtk_css_computed_values_get_value (computed, GTK_CSS_PROPERTY_ANIMATION_ITERATION_COUNT);
+  directions = _gtk_css_computed_values_get_value (computed, GTK_CSS_PROPERTY_ANIMATION_DIRECTION);
+  play_states = _gtk_css_computed_values_get_value (computed, GTK_CSS_PROPERTY_ANIMATION_PLAY_STATE);
+  fill_modes = _gtk_css_computed_values_get_value (computed, GTK_CSS_PROPERTY_ANIMATION_FILL_MODE);
+
+  for (i = 0; i < _gtk_css_array_value_get_n_values (animations); i++)
+    {
+      GtkStyleAnimation *animation;
+      GtkCssKeyframes *keyframes;
+      const char *name;
+      
+      name = _gtk_css_ident_value_get (_gtk_css_array_value_get_nth (animations, i));
+      if (g_ascii_strcasecmp (name, "none") == 0)
+        continue;
+
+      animation = gtk_css_animated_values_find_animation (values, name);
+      if (animation)
+        continue;
+
+      keyframes = _gtk_style_provider_private_get_keyframes (provider, name);
+      if (keyframes == NULL)
+        continue;
+
+      keyframes = _gtk_css_keyframes_compute (keyframes, context);
+
+      animation = _gtk_css_animation_new (name,
+                                          keyframes,
+                                          timestamp + _gtk_css_number_value_get (_gtk_css_array_value_get_nth (delays, i), 100) * G_USEC_PER_SEC,
+                                          _gtk_css_number_value_get (_gtk_css_array_value_get_nth (durations, i), 100) * G_USEC_PER_SEC,
+                                          _gtk_css_array_value_get_nth (timing_functions, i),
+                                          _gtk_css_direction_value_get (_gtk_css_array_value_get_nth (directions, i)),
+                                          _gtk_css_play_state_value_get (_gtk_css_array_value_get_nth (play_states, i)),
+                                          _gtk_css_fill_mode_value_get (_gtk_css_array_value_get_nth (fill_modes, i)),
+                                          _gtk_css_number_value_get (_gtk_css_array_value_get_nth (iteration_counts, i), 100));
+      values->animations = g_slist_prepend (values->animations, animation);
+    }
+}
+
 /* PUBLIC API */
 
 static void
 gtk_css_animated_values_start_animations (GtkCssAnimatedValues *values,
                                           gint64                timestamp,
-                                          GtkCssComputedValues *source)
+                                          GtkCssComputedValues *source,
+                                          GtkStyleContext      *context)
 {
   gtk_css_animated_values_start_transitions (values, timestamp, source);
+  gtk_css_animated_values_start_css_animations (values, timestamp, context);
 }
 
 GtkCssComputedValues *
 _gtk_css_animated_values_new (GtkCssComputedValues *computed,
                               GtkCssComputedValues *source,
-                              gint64                timestamp)
+                              gint64                timestamp,
+                              GtkStyleContext      *context)
 {
   GtkCssAnimatedValues *values;
   GtkCssValue *value;
@@ -237,6 +317,7 @@ _gtk_css_animated_values_new (GtkCssComputedValues *computed,
 
   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (computed), NULL);
   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (source), NULL);
+  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
 
   values = g_object_new (GTK_TYPE_CSS_ANIMATED_VALUES, NULL);
 
@@ -264,7 +345,7 @@ _gtk_css_animated_values_new (GtkCssComputedValues *computed,
   GTK_CSS_COMPUTED_VALUES (values)->depends_on_color = _gtk_bitmask_copy (computed->depends_on_color);
   GTK_CSS_COMPUTED_VALUES (values)->depends_on_font_size = _gtk_bitmask_copy (computed->depends_on_font_size);
 
-  gtk_css_animated_values_start_animations (values, timestamp, source);
+  gtk_css_animated_values_start_animations (values, timestamp, source, context);
 
   ignore = _gtk_css_animated_values_advance (values, timestamp);
   _gtk_bitmask_free (ignore);
index 50ef4d8e47c3f628f75770bb146973ffa3dcb481..1877d3bc602f9f39a3b9171788b6dc57d187e7c1 100644 (file)
@@ -52,7 +52,8 @@ GType                   _gtk_css_animated_values_get_type             (void) G_G
 
 GtkCssComputedValues *  _gtk_css_animated_values_new                  (GtkCssComputedValues     *computed,
                                                                        GtkCssComputedValues     *source,
-                                                                       gint64                    timestamp);
+                                                                       gint64                    timestamp,
+                                                                       GtkStyleContext          *context);
 
 GtkBitmask *            _gtk_css_animated_values_advance              (GtkCssAnimatedValues     *values,
                                                                        gint64                    timestamp) G_GNUC_WARN_UNUSED_RESULT;
diff --git a/gtk/gtkcssanimation.c b/gtk/gtkcssanimation.c
new file mode 100644 (file)
index 0000000..765bb4e
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright © 2012 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "config.h"
+
+#include "gtkcssanimationprivate.h"
+
+#include "gtkcsseasevalueprivate.h"
+
+#include <math.h>
+
+G_DEFINE_TYPE (GtkCssAnimation, _gtk_css_animation, GTK_TYPE_STYLE_ANIMATION)
+
+/* NB: Return value can be negative and +-Inf */
+static double
+gtk_css_animation_get_iteration (GtkCssAnimation *animation,
+                                 gint64           for_time_us)
+{
+  gint64 elapsed;
+  double iterations;
+
+  elapsed = for_time_us - animation->timestamp;
+  iterations = (double) elapsed / animation->duration;
+
+  return iterations;
+}
+
+static gboolean
+gtk_css_animation_is_executing_at_iteration (GtkCssAnimation *animation,
+                                             double           iteration)
+{
+  switch (animation->fill_mode)
+    {
+    case GTK_CSS_FILL_NONE:
+      return iteration >= 0 && iteration <= animation->iteration_count;
+    case GTK_CSS_FILL_FORWARDS:
+      return iteration >= 0;
+    case GTK_CSS_FILL_BACKWARDS:
+      return iteration <= animation->iteration_count;
+    case GTK_CSS_FILL_BOTH:
+      return TRUE;
+    default:
+      g_return_val_if_reached (FALSE);
+    }
+}
+
+static double
+gtk_css_animation_get_progress_from_iteration (GtkCssAnimation *animation,
+                                               double           iteration)
+{
+  double d;
+
+  iteration = CLAMP (iteration, 0, animation->iteration_count);
+
+  switch (animation->direction)
+    {
+    case GTK_CSS_DIRECTION_NORMAL:
+      if (iteration == animation->iteration_count)
+        return 1;
+      else
+        return iteration - floor (iteration);
+    case GTK_CSS_DIRECTION_REVERSE:
+      if (iteration == animation->iteration_count)
+        return 1;
+      else
+        return ceil (iteration) - iteration;
+    case GTK_CSS_DIRECTION_ALTERNATE:
+      d = floor (iteration);
+      if (fmod (d, 2))
+        return iteration - d;
+      else
+        return 1 + d - iteration;
+    case GTK_CSS_DIRECTION_ALTERNATE_REVERSE:
+      d = floor (iteration);
+      if (fmod (d, 2))
+        return 1 + d - iteration;
+      else
+        return iteration - d;
+    default:
+      g_return_val_if_reached (0);
+    }
+}
+
+static GtkBitmask *
+gtk_css_animation_set_values (GtkStyleAnimation    *style_animation,
+                              GtkBitmask           *changed,
+                              gint64                for_time_us,
+                              GtkCssComputedValues *values)
+{
+  GtkCssAnimation *animation = GTK_CSS_ANIMATION (style_animation);
+  double iteration, progress;
+  guint i;
+
+  iteration = gtk_css_animation_get_iteration (animation, for_time_us);
+
+  if (!gtk_css_animation_is_executing_at_iteration (animation, iteration))
+    return changed;
+
+  progress = gtk_css_animation_get_progress_from_iteration (animation, iteration);
+  progress = _gtk_css_ease_value_transform (animation->ease, progress);
+  
+  for (i = 0; i < _gtk_css_keyframes_get_n_properties (animation->keyframes); i++)
+    {
+      GtkCssValue *value;
+      guint property_id;
+      
+      property_id = _gtk_css_keyframes_get_property_id (animation->keyframes, i);
+
+      value = _gtk_css_keyframes_get_value (animation->keyframes,
+                                            i,
+                                            progress,
+                                            _gtk_css_computed_values_get_value (values, i));
+      /* XXX: Is using 0 correct here? */
+      _gtk_css_computed_values_set_value (values, property_id, value, 0, NULL);
+      _gtk_css_value_unref (value);
+      
+      changed = _gtk_bitmask_set (changed, property_id, TRUE);
+    }
+
+  return changed;
+}
+
+static gboolean
+gtk_css_animation_is_finished (GtkStyleAnimation *style_animation,
+                               gint64             at_time_us)
+{
+  return FALSE;
+}
+
+static void
+gtk_css_animation_finalize (GObject *object)
+{
+  GtkCssAnimation *animation = GTK_CSS_ANIMATION (object);
+
+  g_free (animation->name);
+  _gtk_css_keyframes_unref (animation->keyframes);
+  _gtk_css_value_unref (animation->ease);
+
+  G_OBJECT_CLASS (_gtk_css_animation_parent_class)->finalize (object);
+}
+
+static void
+_gtk_css_animation_class_init (GtkCssAnimationClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkStyleAnimationClass *animation_class = GTK_STYLE_ANIMATION_CLASS (klass);
+
+  object_class->finalize = gtk_css_animation_finalize;
+
+  animation_class->set_values = gtk_css_animation_set_values;
+  animation_class->is_finished = gtk_css_animation_is_finished;
+}
+
+static void
+_gtk_css_animation_init (GtkCssAnimation *animation)
+{
+}
+
+GtkStyleAnimation *
+_gtk_css_animation_new (const char      *name,
+                        GtkCssKeyframes *keyframes,
+                        gint64           start_time_us,
+                        gint64           duration_us,
+                        GtkCssValue     *ease,
+                        GtkCssDirection  direction,
+                        GtkCssPlayState  play_state,
+                        GtkCssFillMode   fill_mode,
+                        double           iteration_count)
+{
+  GtkCssAnimation *animation;
+
+  g_return_val_if_fail (name != NULL, NULL);
+  g_return_val_if_fail (keyframes != NULL, NULL);
+  g_return_val_if_fail (ease != NULL, NULL);
+  g_return_val_if_fail (iteration_count >= 0, NULL);
+
+  animation = g_object_new (GTK_TYPE_CSS_ANIMATION, NULL);
+
+  animation->name = g_strdup (name);
+  animation->keyframes = _gtk_css_keyframes_ref (keyframes);
+  animation->timestamp = start_time_us;
+  animation->duration = duration_us;
+  animation->ease = _gtk_css_value_ref (ease);
+  animation->direction = direction;
+  animation->play_state = play_state;
+  animation->fill_mode = fill_mode;
+  animation->iteration_count = iteration_count;
+
+  return GTK_STYLE_ANIMATION (animation);
+}
+
+const char *
+_gtk_css_animation_get_name (GtkCssAnimation *animation)
+{
+  g_return_val_if_fail (GTK_IS_CSS_ANIMATION (animation), NULL);
+
+  return animation->name;
+}
diff --git a/gtk/gtkcssanimationprivate.h b/gtk/gtkcssanimationprivate.h
new file mode 100644 (file)
index 0000000..f992605
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2012 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#ifndef __GTK_CSS_ANIMATION_PRIVATE_H__
+#define __GTK_CSS_ANIMATION_PRIVATE_H__
+
+#include "gtkstyleanimationprivate.h"
+
+#include "gtkcsskeyframesprivate.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CSS_ANIMATION           (_gtk_css_animation_get_type ())
+#define GTK_CSS_ANIMATION(obj)           (G_TYPE_CHECK_INSTANCE_CAST (obj, GTK_TYPE_CSS_ANIMATION, GtkCssAnimation))
+#define GTK_CSS_ANIMATION_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST (cls, GTK_TYPE_CSS_ANIMATION, GtkCssAnimationClass))
+#define GTK_IS_CSS_ANIMATION(obj)        (G_TYPE_CHECK_INSTANCE_TYPE (obj, GTK_TYPE_CSS_ANIMATION))
+#define GTK_IS_CSS_ANIMATION_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE (obj, GTK_TYPE_CSS_ANIMATION))
+#define GTK_CSS_ANIMATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CSS_ANIMATION, GtkCssAnimationClass))
+
+typedef struct _GtkCssAnimation           GtkCssAnimation;
+typedef struct _GtkCssAnimationClass      GtkCssAnimationClass;
+
+struct _GtkCssAnimation
+{
+  GtkStyleAnimation parent;
+
+  char            *name;
+  GtkCssKeyframes *keyframes;
+  GtkCssValue     *ease;
+  gint64           timestamp;           /* elapsed time when paused, start time when playing (can be negative) */
+  gint64           duration;            /* duration of 1 cycle */
+  GtkCssDirection  direction;
+  GtkCssPlayState  play_state;
+  GtkCssFillMode   fill_mode;
+  double           iteration_count;
+};
+
+struct _GtkCssAnimationClass
+{
+  GtkStyleAnimationClass parent_class;
+};
+
+GType                   _gtk_css_animation_get_type        (void) G_GNUC_CONST;
+
+GtkStyleAnimation *     _gtk_css_animation_new             (const char         *name,
+                                                            GtkCssKeyframes    *keyframes,
+                                                            gint64              start_time_us,
+                                                            gint64              duration_us,
+                                                            GtkCssValue        *ease,
+                                                            GtkCssDirection     direction,
+                                                            GtkCssPlayState     play_state,
+                                                            GtkCssFillMode      fill_mode,
+                                                            double              iteration_count);
+
+const char *            _gtk_css_animation_get_name        (GtkCssAnimation   *animation);
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_ANIMATION_PRIVATE_H__ */
index 69de53e898838ffabde91f59e86d932c761d2873..227466219b211e4ba05793afb29828615c8c2a83 100644 (file)
@@ -3057,7 +3057,8 @@ gtk_style_context_start_animations (GtkStyleContext      *context,
   animated = style_data_new ();
   animated->store = _gtk_css_animated_values_new (style_data_lookup (context)->store,
                                                   previous,
-                                                  timestamp);
+                                                  timestamp,
+                                                  context);
 
   if (_gtk_css_animated_values_is_finished (GTK_CSS_ANIMATED_VALUES (animated->store)))
     {